#include "rtc.h"

/** Wait until data in rtc is valid.
 * Data is valid until +/- 240 usec after this function returns,
 * thus a read/write can be done to the RTC within this time period
 */
void rtc_valid()
{
	int old_pic1 = inportb(PIC1_MASK);
	int old_pic2 = inportb(PIC2_MASK);
	disable_pics();
	
	outportb(RTC_ADDR_REG, RTC_STAT_A);
	
	while(inportb(RTC_DATA_REG) & RTC_UIP)
	;
	
	enable_pics(old_pic1, old_pic2);
}

/** Returns rtc data from I/O address add. Doesn't check valid bit
 */
Byte read_rtc(Byte add)
{
	int old_pic1 = inportb(PIC1_MASK);
	int old_pic2 = inportb(PIC2_MASK);
	disable_pics();
	
	outportb(RTC_ADDR_REG, add);
	
	Byte value = inportb(RTC_DATA_REG);
	
	enable_pics(old_pic1, old_pic2);
	
	return value;
}

/** Returns rtc data from I/O address add. Check valid bit
 */
Byte read_rtcv(Byte add)
{
	rtc_valid();
	
	return read_rtc(add);
}

/** converts BCD (Binary Coded Decimal) to decimal
 */
Byte bcd2dec(Byte i)
{
	return (i & 0x0F) + 10*( (i >> 4) & 0x0F);
}

/** converts decimal to BCD (Binary Coded Decimal)
 */
Byte dec2bcd(Byte i)
{
	return (i%10) + ( ( (i/10)%10) << 4);
}

/** Read RTC stored date
 *
 * Uses read_rtcv() and bcd2dec()
 */
void rtc_read_date(RTC_DATE *date)
{
	date->year = bcd2dec(read_rtcv(YEAR));
	date->month = bcd2dec(read_rtcv(MONTH));
	date->day = bcd2dec(read_rtcv(MONTH_DAY));
}

/** Read RTC stored time
 *
 * Uses read_rtcv() and bcd2dec()
 */
void rtc_read_time(RTC_TIME *time)
{
	time->hour = bcd2dec(read_rtcv(HOUR));
	time->min = bcd2dec(read_rtcv(MIN));
	time->sec = bcd2dec(read_rtcv(SEC));
}

/** Read RTC stored alarm
 *
 * Uses read_rtcv() and bcd2dec()
 */
void rtc_read_alarm(RTC_TIME *time)
{
	time->hour = bcd2dec(read_rtcv(HOUR_ALARM));
	time->min = bcd2dec(read_rtcv(MIN_ALARM));
	time->sec = bcd2dec(read_rtcv(SEC_ALARM));
}

/** Write value to I/O address add. Doesn't check valid bit
 */
void write_rtc(Byte add, Byte value)
{
	int old_pic1 = inportb(PIC1_MASK);
	int old_pic2 = inportb(PIC2_MASK);
	disable_pics();

	outportb(RTC_ADDR_REG,add);
	outportb(RTC_DATA_REG, value);
	
	enable_pics(old_pic1, old_pic2);
}

/** Write value to I/O address add. Check valid bit
 */
void write_rtcv(Byte add, Byte value)
{
	rtc_valid();
	
	write_rtc(add,value);
}

/** Write alarm to RTC
 *
 * Uses write_rtcv() and dec2bcd()
 */
void rtc_write_alarm(RTC_TIME *time)
{
	write_rtcv(HOUR_ALARM, dec2bcd(time->hour));
	write_rtcv(MIN_ALARM, dec2bcd(time->min));
	write_rtcv(SEC_ALARM, dec2bcd(time->sec));
}

/** Enables all RTC interruptions
 *
 */
void rtc_ints_enable()
{
	write_rtc(RTC_STAT_B, (read_rtc(RTC_STAT_B) | RTC_PIE | RTC_AIE | RTC_UIE));
	read_rtc(RTC_STAT_C);
}

/** Disables all RTC interruptions
 *
 */
void rtc_ints_disable()
{
	write_rtc(RTC_STAT_B, (read_rtc(RTC_STAT_B) & ~(RTC_PIE | RTC_AIE | RTC_UIE)));
}
